﻿using JamesZhao.Engine.Graphics.Geometry;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;

namespace JamesZhao.Engine.Graphics
{
    public class GeometryBuffer
    {
        public GeometryBuffer(GraphicsDevice device)
        {
            Device = device;
        }

        public int TriangleCount { get; private set; }


        public GraphicsDevice Device { get; set; }


        public Buffer VertexBuffer { get; private set; }

        public Buffer IndexBuffer { get; private set; }

        public void InitializeFrom(MeshData meshData)
        {
            SetupBuffers(meshData);
        }


        private static DataStream CreateVertexDataStream(MeshData meshData)
        {
            var dataStream = new DataStream(meshData.Vertices.Count*BufferConstants.VertexBufferElementSize, true, true);
            foreach (Vertex vert in meshData.Vertices)
            {
                dataStream.Write(vert.Position);
                dataStream.Write(vert.Normal);
                dataStream.Write(vert.Color);
            }
            dataStream.Position = 0;

            return dataStream;
        }

        private static DataStream CreateIndexDataStream(MeshData meshData)
        {
            var dataStream =
                new DataStream(
                    meshData.Triangles.Count*BufferConstants.VertsPerTriangle*BufferConstants.IndexBufferElementSize,
                    true, true);
            foreach (Triangle tri in meshData.Triangles)
            {
                dataStream.WriteRange(tri.Verts);
            }
            dataStream.Position = 0;
            return dataStream;
        }

        protected void SetupBuffers(MeshData meshData)
        {
            BufferDescription vertexBufferDescription = CreateBufferDescription(BindFlags.VertexBuffer,
                                                                                meshData.Vertices.Count*
                                                                                BufferConstants.VertexBufferElementSize);
            VertexBuffer = new Buffer(Device.Device, CreateVertexDataStream(meshData), vertexBufferDescription);

            BufferDescription indexBufferDescription = CreateBufferDescription(BindFlags.IndexBuffer,
                                                                               meshData.Triangles.Count*3*
                                                                               BufferConstants.IndexBufferElementSize);
            IndexBuffer = new Buffer(Device.Device, CreateIndexDataStream(meshData), indexBufferDescription);

            TriangleCount = meshData.Triangles.Count;
        }

        private static BufferDescription CreateBufferDescription(BindFlags bindFlags, int size)
        {
            return new BufferDescription
                       {
                           BindFlags = bindFlags,
                           CpuAccessFlags =
                               CpuAccessFlags.None,
                           OptionFlags =
                               ResourceOptionFlags.
                               None,
                           SizeInBytes = size,
                           Usage =
                               ResourceUsage.Default
                       };
        }

        internal void ApplyBuffersAndTopology(GraphicsDevice device)
        {
            device.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            device.ImmediateContext.InputAssembler.SetVertexBuffers(0,
                                                                    new VertexBufferBinding(VertexBuffer,
                                                                                            BufferConstants.
                                                                                                VertexBufferElementSize,
                                                                                            0));
            device.ImmediateContext.InputAssembler.SetIndexBuffer(IndexBuffer, Format.R32_UInt, 0);
        }
    }
}